package main

import (
	"bufio"
	"crypto/md5"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"sync"

	"golang.org/x/text/encoding/simplifiedchinese"
)

// getFileMD5 获取文件MD5值
func getFileMD5(filePath string) (string, error) {
	f, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer f.Close()

	md5hash := md5.New()
	if _, err := io.Copy(md5hash, f); err != nil {
		return "", err
	}
	md5string := fmt.Sprintf("%x", md5hash.Sum(nil))
	return md5string, nil
}

// 定义变量只会赋值一次
var once sync.Once
var pathDelimiter string

// Exists 判断所给路径文件/文件夹是否存在
func Exists(path string) bool {
	_, err := os.Stat(path) //os.Stat获取文件信息
	if err != nil {
		return os.IsExist(err)
	}
	return true
}

// IsDir 判断所给路径是否为文件夹
func IsDir(path string) bool {
	s, err := os.Stat(path)
	if err != nil {
		return false
	}
	return s.IsDir()
}

// IsFile 判断所给路径是否为文件
func IsFile(path string) bool {
	return !IsDir(path)
}

// getFiles 获取所有文件 folder 目录名
func getFiles(folder string, fileList *[]string) {
	pathDelimiter := getPathDelimiter()
	files, _ := ioutil.ReadDir(folder)
	for _, file := range files {
		if file.IsDir() {
			getFiles(folder+pathDelimiter+file.Name(), fileList)
		} else {
			fileTmp := folder + pathDelimiter + file.Name()
			*fileList = append(*fileList, fileTmp)
		}
	}
}

// getPathDelimiter 获取当前的路径分割符号，使用单例只获取一次即可
func getPathDelimiter() string {
	once.Do(func() {
		// 判断当前执行环境是Win或者Linux处理路径
		ostype := runtime.GOOS
		if ostype == "windows" {
			pathDelimiter = "\\"
		} else if ostype == "linux" {
			pathDelimiter = "/"
		}
		log.Printf("当前工作环境：%s ; 目录分割符：%s", ostype, pathDelimiter)
	})
	return pathDelimiter
}

// 获取当前工作路径
func getCurrentPath() string {
	currentPath, _ := os.Getwd()
	//log.Printf("当前工作目录: %s", currentPath)
	return currentPath
}

// 字符串写入文件
func writeStringToFile(fileName string, writeInfo string) {
	_ = IfNoFileToCreate(fileName)
	// O_RDWR:打开文件读写 O_APPEND:在写文件时追加数据  O_CREATE:如果不存在文件，创建一个新文件 O_TRUNC:在打开时截断常规可写文件。
	f, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE, 0666) //打开文件

	if err != nil {
		log.Printf("打开文件失败:%+v", err)
		return
	}

	defer f.Close()

	// 将文件写进去
	if _, err = io.WriteString(f, writeInfo); err != nil {
		log.Printf("WriteStringToFile写入文件失败:%+v", err)
		return
	}
	log.Printf("WriteStringToFile写入文件成功")
}

// IfNoFileToCreate 文件不存在就创建文件
func IfNoFileToCreate(fileName string) (file *os.File) {
	var f *os.File
	var err error
	if !Exists(fileName) {
		f, err = os.Create(fileName)
		if err != nil {
			return
		}
		log.Printf("IfNoFileToCreate 函数成功创建文件:%s", fileName)
		defer f.Close()
	}
	return f
}

// ReadFileFromLine 按行读取文件内容
func ReadFileFromLine(filePath string, handle func(string)) error {
	f, err := os.Open(filePath)

	if err != nil {
		return err
	}

	defer f.Close()

	buf := bufio.NewReader(f)

	for {
		lineString, err := buf.ReadString('\n')
		handle(lineString)
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
	}
}

// ReadFileToString 先从文件读取到file, 在从file读取到Reader中，从Reader读取到buf, buf最终追加到[]byte
func ReadFileToString(fileName string) string {
	f, err := os.Open(fileName)
	if err != nil {
		log.Printf("打开文件失败①:%#v", err)
		panic(err)
	}
	defer f.Close()
	r := bufio.NewReader(f)
	var dataBlocks []byte
	buf := make([]byte, 102400) // 每次读取字节数
	for {
		n, err := r.Read(buf) // 读取字节数 n
		if err != nil {
			if err == io.EOF {
				// 判断文件读取结束
				break
			}
			log.Printf("打开文件读取内容失败②:%#v", err)
		}
		dataBlocks = append(dataBlocks, buf[:n]...) // 注意有人这里[:n] 是读的字节数赋值，最后一次读取可能小于buf定义量
	}
	if IsUtf8(dataBlocks) {
		log.Printf("[INFO:]读取的文件是 utf-8 格式直接使用!")
		return string(dataBlocks)
	}
	// 将 gbk 转换为utf-8
	utf8Data, _ := simplifiedchinese.GBK.NewDecoder().Bytes(dataBlocks)
	log.Printf("[INFO:]读取的文件是 gbk 格式需要转成 utf-8 后使用!")
	return string(utf8Data)
}

type fileAllName struct {
	FileName   string
	SuffixName string
	PrefixName string
}

// GetFileSuffixName 获取文件名称信息 prefixName 前缀  suffixName 后缀 fileName 文件名
func GetFileFullNameSuffixNamePrefixName(filePathName string) fileAllName {
	// paths, fileName := filepath.Split(filePathName)
	fileName := filepath.Base(filePathName)
	suffixName := path.Ext(filePathName)
	prefixName := strings.TrimSuffix(fileName, suffixName)
	// return map[string]string{"fileName": fileName, "suffixName": suffixName, "prefixName": prefixName}
	return fileAllName{fileName, suffixName, prefixName}
}
